//
//  NotificationCenter.m
//  TestApp
//
//  Created by lianglibaoM2 on 2024/8/2.
//

#import "NotificationCenter.h"
#import <objc/runtime.h>
#import <os/lock.h>

@interface NotificationProxy : NSProxy

@property (nonatomic, weak) id target;
@property (nonatomic) SEL selector;
@property (nonatomic, weak) NotificationCenter *notificationCenter;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, weak) id object;

- (instancetype)initWithTarget:(id)target selector:(SEL)selector notificationCenter:(NotificationCenter *)notificationCenter name:(NSString *)name object:(id)object;

@end

@implementation NotificationProxy

- (instancetype)initWithTarget:(id)target selector:(SEL)selector notificationCenter:(NotificationCenter *)notificationCenter name:(NSString *)name object:(id)object {
    _target = target;
    _selector = selector;
    _notificationCenter = notificationCenter;
    _name = name;
    _object = object;
    return self;
}

- (void)dealloc {
    //[self.notificationCenter removeObserver:_target name:_name object:_object];
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    if ([_target respondsToSelector:_selector]) {
        [invocation setSelector:_selector];
        [invocation invokeWithTarget:_target];
    }
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [_target methodSignatureForSelector:_selector];
}

@end

@interface NotificationCenter ()

@property (nonatomic, strong) NSMutableDictionary<NSString *, NSHashTable<NotificationProxy *> *> *observers;
@property (nonatomic) os_unfair_lock lock;

@end

@implementation NotificationCenter

+ (instancetype)sharedCenter {
    static NotificationCenter *center;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        center = [[NotificationCenter alloc] init];
    });
    return center;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        _observers = [NSMutableDictionary dictionary];
        _lock = OS_UNFAIR_LOCK_INIT;
    }
    return self;
}

- (void)addObserver:(id)observer selector:(SEL)selector name:(NSString *)name object:(id)object {
    os_unfair_lock_lock(&_lock);
    NSHashTable *existList = [NSHashTable hashTableWithOptions:NSPointerFunctionsStrongMemory];
    if (self.observers[name]) {
        for (NotificationProxy *proxy in self.observers[name]) {
            if (proxy.target != nil) {
                [existList addObject:proxy];
            }
        }
    }
    NotificationProxy *proxy = [[NotificationProxy alloc] initWithTarget:observer selector:selector notificationCenter:self name:name object:object];
    [existList addObject:proxy];
    self.observers[name] = existList;
    os_unfair_lock_unlock(&_lock);
}

- (void)removeObserver:(id)observer {
    os_unfair_lock_lock(&_lock);
    NSArray <NSHashTable<NotificationProxy *> *> *list = self.observers.allValues;
    for (NSHashTable<NotificationProxy *> *proxies in list) {
        for (NotificationProxy *proxy in proxies) {
            if (proxy.target == observer) {
                [proxies removeObject:proxy];
                break;
            }
        }
    }
    os_unfair_lock_unlock(&_lock);
}

- (void)removeObserver:(id)observer name:(NSString *)name object:(id)object {
    os_unfair_lock_lock(&_lock);
    NSHashTable<NotificationProxy *> *proxies = self.observers[name];
    for (NotificationProxy *proxy in proxies) {
        if (proxy.target == observer && proxy.object == object) {
            [proxies removeObject:proxy];
            break;
        }
    }
    os_unfair_lock_unlock(&_lock);
}

- (void)postNotificationName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo {
    os_unfair_lock_lock(&_lock);
    NSHashTable<NotificationProxy *> *proxies = self.observers[name];
    NSNotification *notification = [[NSNotification alloc] initWithName:name object:object userInfo:userInfo];
    for (NotificationProxy *proxy in proxies) {
        if (proxy.object == nil || proxy.object == object) {
            NSMethodSignature *methodSignature = [proxy.target methodSignatureForSelector:proxy.selector];
            NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
            [invocation setSelector:proxy.selector];
            [invocation setTarget:proxy.target];
            if (methodSignature.numberOfArguments > 2) {
                [invocation setArgument:&notification atIndex:2];
            }
            [invocation invoke];
        }
    }
    os_unfair_lock_unlock(&_lock);
}

@end
